home *** CD-ROM | disk | FTP | other *** search
/ BBS in a Box 7 / BBS in a Box - Macintosh - Volume VII (BBS in a Box) (January 1993).iso / Files / Hyper / N-O / NewSTAK.cpt / NewSTAK / card_3330.txt < prev    next >
Text File  |  1989-12-03  |  14KB  |  534 lines

  1. -- card: 3330 from stack: in
  2. -- bmap block id: 0
  3. -- flags: 0000
  4. -- background id: 2805
  5. -- name: 
  6.  
  7.  
  8. -- part contents for background part 9
  9. ----- text -----
  10. Here is the complete text of the NewSTAK XCMD, as written for Lightspeed Pascal version 2.03.
  11. -------------------------------------------------------------------------
  12. {NewSTAK creates a new stack with name passed in first parameter.}
  13. {If no first parameter, it uses "New " plus current stack name.}
  14. {If name is not a pathname (with colons) it will use the current folder.}
  15. {Second parameter is the number of the STAK resource to use as the data fork.}
  16. {If no second parameter, it will use the first STAK resource it finds.}
  17. {The entire resource fork of the parent stack is copied to the daughter stack.}
  18. {Operating System and other errors are passed back in "the Result".}
  19.  
  20. unit Main;
  21.  
  22. interface
  23.  type
  24.   XCmdPtr = ^XCmdBlock;
  25.   XCmdBlock = record
  26.     paramCount: INTEGER;
  27.     params: array[1..16] of Handle;
  28.     returnValue: Handle;
  29.     passFlag: BOOLEAN;
  30.  
  31.     entryPoint: ProcPtr;    { to call back to HyperCard }
  32.     request: INTEGER;
  33.     result: INTEGER;
  34.     inArgs: array[1..8] of LongInt;
  35.     outArgs: array[1..4] of LongInt;
  36.    end;
  37.  
  38.  procedure Main (ParamPtr: XCmdPtr);
  39.  
  40. implementation
  41.  
  42. {=================================MAIN}
  43.  procedure NewSTAK (ParamPtr: XCmdPtr);
  44.  FORWARD;
  45.  
  46.  procedure Main;
  47.  begin
  48.   NewSTAK(ParamPtr);
  49.  end;
  50.  
  51.  procedure NewSTAK;
  52.  
  53.   const
  54.    CR = chr(13);
  55.    Unspecified = -32761;{STAK resource to use if not specified by user}
  56.  
  57.  { request codes for sending commands back to Hypercard}
  58.    xreqSendCardMessage = 1;
  59.    xreqEvalExpr = 2;
  60.    xreqPasToZero = 7;
  61.    xreqZeroToPas = 8;
  62.    xreqStrToNum = 10;
  63.    xreqNumToStr = 14;
  64.  
  65.   type
  66.    Str19 = string[19];
  67.    Str31 = string[31];
  68.  
  69.   var
  70.    OldStackPath, OldStackName, NewStackName, NewStackPath: str255;
  71.    STAKResID: longint;
  72.    ReturnString: str255;
  73.    pBlock: HParamBlockRec;
  74.    theParms: HParmBlkPtr;
  75.    AnyErr: OSErr;
  76.    OldResRefNum, NewResRefNum: integer;
  77.  
  78. {=================================DoJsr}
  79. { Jump subroutine to a procedure.   Pop address into A0, JSR (A0) }
  80.   procedure DoJsr (addr: ProcPtr);
  81.   inline
  82.    $205F, $4E90;
  83.  
  84. {=================================SendCardMessage}
  85. {  Send a HyperCard message (a command with arguments) to the current card. }
  86.   procedure SendCardMessage (msg: Str255);
  87.  
  88.   begin
  89.    with paramPtr^ do
  90.     begin
  91.      inArgs[1] := ORD(@msg);
  92.      request := xreqSendCardMessage;
  93.      DoJsr(entryPoint);
  94.     end;
  95.   end;
  96.  
  97. {=================================ZeroToPas}
  98. {Fill the Pascal string with the contents of the zero-terminated}
  99. {   string.  You create the Pascal string and pass it in as a VAR }
  100. {   parameter.  Useful for converting the arguments of any XCMD to }
  101. {   Pascal strings.}
  102.  
  103.   procedure ZeroToPas (zeroStr: Ptr;
  104.      var pasStr: Str255);
  105.   begin
  106.    with paramPtr^ do
  107.     begin
  108.      inArgs[1] := ORD(zeroStr);
  109.      inArgs[2] := ORD(@pasStr);
  110.      request := xreqZeroToPas;
  111.      DoJsr(entryPoint);
  112.     end;
  113.   end;
  114.  
  115. {=================================PasToZero}
  116. {  Convert a Pascal string to a zero-terminated string.  Returns a handle}
  117. {   to a new zero-terminated string.  The caller must dispose the handle. }
  118.  
  119.   function PasToZero (str: Str255): Handle;
  120.   begin
  121.    with paramPtr^ do
  122.     begin
  123.      inArgs[1] := ORD(@str);
  124.      request := xreqPasToZero;
  125.      DoJsr(entryPoint);
  126.      PasToZero := Handle(outArgs[1]);
  127.     end;
  128.   end;
  129.  
  130. {=================================EvalExpr}
  131. {  Evaluate a HyperCard expression and return the answer.  The answer is}
  132. {   a handle to a zero-terminated string, which must be disposed of. }
  133.  
  134.   function EvalExpr (expr: Str255): Handle;
  135.   begin
  136.    with paramPtr^ do
  137.     begin
  138.      inArgs[1] := ORD(@expr);
  139.      request := xreqEvalExpr;
  140.      DoJsr(entryPoint);
  141.      EvalExpr := Handle(outArgs[1]);
  142.     end;
  143.   end;
  144.  
  145. {=================================StrToNum}
  146. {  Convert a string of ASCII decimal digits to a signed long integer.}
  147. {   Negative sign is allowed.  }
  148.   function StrToNum (str: Str31): LongInt;
  149.   begin
  150.    with paramPtr^ do
  151.     begin
  152.      inArgs[1] := ORD4(@str);
  153.      request := xreqStrToNum;
  154.      DoJsr(entryPoint);
  155.      StrToNum := outArgs[1];
  156.     end;
  157.   end;
  158.  
  159. {=================================NumToStr}
  160. {  Convert a signed long integer to a Pascal string.  }
  161.   function NumToStr (num: LongInt): Str31;
  162.  
  163.    var
  164.     str: Str31;
  165.  
  166.   begin
  167.    with paramPtr^ do
  168.     begin
  169.      inArgs[1] := num;
  170.      inArgs[2] := ORD(@str);
  171.      request := xreqNumToStr;
  172.      DoJsr(entryPoint);
  173.      NumToStr := str;
  174.     end;
  175.   end;
  176.  
  177. {=================================CreateNewFile}
  178. {Create a new file (both forks) under the new stack name.}
  179.   function CreateNewFile: boolean;
  180.  
  181.    var
  182.     theSpecs: FInfo;
  183.  
  184.   begin
  185.    CreateNewFile := FALSE;
  186.  
  187.    CreateResFile(NewStackPath);
  188.    AnyErr := ResError;
  189.    if AnyErr <> NoErr then
  190.     begin
  191.      case AnyErr of
  192.      -49, -48: 
  193.      ReturnString := Concat('File already exists: ', NewStackPath);
  194.      otherwise
  195.      ReturnString := Concat('Error ', NumToStr(AnyErr), ' trying to create new file name.');
  196.      end;
  197.      EXIT(CreateNewFile);
  198.     end;
  199.  
  200. {Set the creator and file type.}
  201.    AnyErr := GetFInfo(NewStackPath, 0, theSpecs);
  202.    if AnyErr <> NoErr then
  203.     begin
  204.      ReturnString := Concat('Error ', NumToStr(AnyErr), ' reading new file''s Finder info.');
  205.      EXIT(CreateNewFile);
  206.     end;
  207.  
  208.    with theSpecs do
  209.     begin
  210.      fdType := 'STAK';
  211.      fdCreator := 'WILD';
  212.     end;
  213.    AnyErr := SetFInfo(NewStackPath, 0, theSpecs);
  214.    if AnyErr <> NoErr then
  215.     begin
  216.      ReturnString := Concat('Error ', NumToStr(AnyErr), ' setting new file Creator and Type.');
  217.      EXIT(CreateNewFile);
  218.     end;
  219.  
  220.    CreateNewFile := TRUE;
  221.   end;{CreateNewFile}
  222.  
  223. {=================================ResourceLen}
  224. {Find out how large the resource fork is.}
  225.   function ResourceLen: longint;
  226.  
  227.    var
  228.     pBlock: ParamBlockRec;
  229.     theParms: ParmBlkPtr;
  230.     OldVolRefNum: integer;
  231.  
  232.   begin
  233.    AnyErr := GetVRefNum(OldResRefNum, OldVolRefNum);
  234.    if AnyErr <> NoErr then
  235.     begin
  236.      ReturnString := Concat('Error ', NumToStr(AnyErr), ' getting Old VolRefNum.');
  237.      ResourceLen := 0;
  238.      EXIT(ResourceLen);
  239.     end;
  240.  
  241.    theParms := @pBlock;
  242.    with pBlock do
  243.     begin
  244.      ioCompletion := nil;
  245.      ioNamePtr := @OldStackPath;
  246.      ioVRefNum := OldVolRefNum;
  247.      ioFDirIndex := 0;
  248.     end;
  249.  
  250.    AnyErr := PBGetFInfo(theParms, FALSE);
  251.    if AnyErr <> NoErr then
  252.     begin
  253.      ReturnString := Concat('Error ', NumToStr(AnyErr), ' reading len of res fork of ', OldStackPath);
  254.      ResourceLen := 0;
  255.     end
  256.    else
  257.     ResourceLen := pBlock.ioFlRLgLen;
  258.   end;{ResourceLen}
  259.  
  260. {=================================OpenTheDataFile}
  261.   function OpenTheDataFile (FileName: str255;
  262.      VolRefNum: integer;
  263.      var FileRefNum: integer): boolean;
  264.    var
  265.     action: integer;
  266.     mess: string;
  267.     theBlock: HParamBlockRec;
  268.     theName: str255;
  269.  
  270.   begin
  271.    theName := FileName;
  272.    with theBlock do
  273.     begin
  274.      ioCompletion := nil;
  275.      ioNamePtr := @theName;
  276.      ioVRefNum := VolRefNum;{may be zero if DirID used}
  277.      ioPermssn := fsCurPerm;
  278.      ioMisc := nil;{would be ptr to buffer to use}
  279.      ioDirID := 0;{may be 0 if VRefNum used}
  280.     end;
  281.  
  282.    AnyErr := PBHOpen(@theBlock, FALSE);
  283.    if AnyErr = NoErr then
  284.     begin
  285.      FileRefNum := theBlock.ioRefNum;
  286.      OpenTheDataFile := TRUE;
  287.     end
  288.    else
  289.     begin
  290.      ReturnString := Concat('Can''t open data fork ', FileName);
  291.      OpenTheDataFile := FALSE;
  292.     end;
  293.   end;{OpenTheDataFile}
  294.  
  295. {=================================CopyResFork}
  296.   function CopyResFork (ReqBytes: longint): boolean;
  297.  
  298.    var
  299.     biteBytes, bytesCopied: longint;
  300.     Buffer: ptr;
  301.     HCMark: longint;
  302.  
  303.   begin
  304.    CopyResFork := FALSE;
  305.  
  306. {Set up a buffer of no more than 32K.}
  307.    if ReqBytes > 32000 then
  308.     biteBytes := 32000
  309.    else
  310.     biteBytes := ReqBytes;
  311.    Buffer := NewPtr(biteBytes);
  312.    if Buffer = nil then
  313.     begin
  314.      ReturnString := Concat('Can''t allocate pointer of length ', NumToStr(biteBytes));
  315.      EXIT(CopyResFork);
  316.     end;
  317.  
  318. {Look up the current file Mark so we can restore it when we're done.}
  319.    AnyErr := GetFPos(OldResRefNum, HCMark);
  320.    if AnyErr <> NoErr then
  321.     begin
  322.      ReturnString := Concat('Can''t get HC''s File Pos Mark.');
  323.      EXIT(CopyResFork);
  324.     end;
  325.  
  326. {Set the current file Mark to 0.}
  327.    AnyErr := SetFPos(OldResRefNum, fsFromStart, 0);
  328.    if AnyErr <> NoErr then
  329.     begin
  330.      ReturnString := Concat('Can''t set File Pos Mark to start of stack.');
  331.      EXIT(CopyResFork);
  332.     end;
  333.  
  334. {Now read the resource fork in chunks no larger than 32K.}
  335.    bytesCopied := 0;
  336.    repeat
  337.     biteBytes := ReqBytes - bytesCopied;
  338.     if biteBytes > 32000 then
  339.      biteBytes := 32000;
  340.     AnyErr := FSRead(OldResRefNum, biteBytes, Buffer);
  341.     if AnyErr <> NoErr then
  342.      begin
  343.      ReturnString := Concat('Error ', NumToStr(AnyErr), ' reading ', NumToStr(biteBytes), ' of ', NumToStr(ReqBytes), ' bytes from ', OldStackPath);
  344.      AnyErr := FSClose(OldResRefNum);
  345.      DisposPtr(Buffer);
  346.      EXIT(CopyResFork);
  347.      end;
  348.  
  349. {Write the buffer to the new fork.}
  350.     AnyErr := FSWrite(NewResRefNum, biteBytes, Buffer);
  351.     if AnyErr <> NoErr then
  352.      begin
  353.      case AnyErr of
  354.      DskFulErr: 
  355.      ReturnString := 'This volume is full.';
  356.      fLckdErr, wPrErr, vLckdErr, wrPermErr: 
  357.      ReturnString := 'This volume is locked.';
  358.      otherwise
  359.      ReturnString := Concat('Error ', NumToStr(AnyErr), ' writing ', NewStackPath);
  360.      end;
  361.  
  362.      DisposPtr(Buffer);
  363.      EXIT(CopyResFork);
  364.      end;
  365.  
  366.     bytesCopied := bytesCopied + biteBytes;
  367.    until bytesCopied >= ReqBytes;
  368.    DisposPtr(Buffer);
  369.  
  370. {Set the current file Mark to what it was before we mucked with it.}
  371.    AnyErr := SetFPos(OldResRefNum, fsFromStart, HCMark);
  372.    if AnyErr <> NoErr then
  373.     begin
  374.      ReturnString := Concat('Can''t restore File Pos Mark.');
  375.      EXIT(CopyResFork);
  376.     end;
  377.  
  378.    CopyResFork := TRUE;
  379.   end;{CopyResFork}
  380.  
  381. {=================================CopyEverything}
  382.   procedure CopyEverything;
  383.  
  384.    var
  385.     NewDataRefNum: integer;
  386.     resCopyOK: boolean;
  387.     HandSize: longint;
  388.     STAKResHand: handle;
  389.     ResourceForkLength: longint;
  390.  
  391.   begin
  392. {First try to find the chosen STAK resource. If not found, don't bother with resource fork.}
  393.    if STAKResID = Unspecified then
  394.     begin
  395.      STAKResHand := GetIndResource('STAK', 1);{look at first resource of this type}
  396.      if STAKResHand = nil then
  397.      begin
  398.      ReturnString := 'STAK resource not found or unsufficient RAM.';
  399.      EXIT(CopyEverything);
  400.      end;
  401.     end
  402.    else
  403.     begin
  404.      STAKResHand := GetResource('STAK', STAKResID);
  405.      if STAKResHand = nil then
  406.      begin
  407.      ReturnString := Concat('STAK ', NumToStr(STAKResID), ' not found or insufficient RAM.');
  408.      EXIT(CopyEverything);
  409.      end;
  410.      if ResError <> NoErr then
  411.      begin
  412.      ReturnString := Concat('Resource Error ', NumToStr(ResError), ' reading STAK resource.');
  413.      EXIT(CopyEverything);
  414.      end;
  415.     end;
  416.  
  417. {Look up length of resource fork now.}
  418.    ResourceForkLength := ResourceLen;
  419.  
  420. {Open data fork of new stack.}
  421.    DetachResource(STAKResHand);{hide it from Resource Manager}
  422.    if not OpenTheDataFile(NewStackPath, 0, NewDataRefNum) then
  423.     begin
  424.      DisposHandle(STAKResHand);
  425.      EXIT(CopyEverything);
  426.     end;
  427.  
  428. {Make copy of STAK resource in data fork of new stack.}
  429.    HandSize := GetHandleSize(STAKResHand);
  430.    AnyErr := FSWrite(NewDataRefNum, HandSize, STAKResHand^);
  431.    if AnyErr <> NoErr then
  432.     case AnyErr of
  433.      DskFulErr: 
  434.      ReturnString := 'This volume is full.';
  435.      fLckdErr, wPrErr, vLckdErr, wrPermErr: 
  436.      ReturnString := 'This volume is locked.';
  437.      otherwise
  438.      ReturnString := Concat('Error ', NumToStr(ResError), ' writing ', NewStackPath);
  439.     end;
  440.  
  441. {Always close the file and flush the volume.}
  442.    DisposHandle(STAKResHand);
  443.    AnyErr := FSClose(NewDataRefNum);
  444.    AnyErr := FlushVol(nil, NewDataRefNum);
  445.    if ReturnString <> '' then
  446.     EXIT(CopyEverything);
  447.  
  448. {And open the copy's resource fork.}
  449.    AnyErr := OpenRF(NewStackPath, 0, NewResRefNum);
  450.    if AnyErr <> NoErr then
  451.     begin
  452.      ReturnString := Concat('Can''t open ', NewStackPath);
  453.      EXIT(CopyEverything);
  454.     end;
  455.  
  456. {Copy the resource fork.}
  457.    resCopyOK := CopyResFork(ResourceForkLength);
  458.  
  459. {Close the new resource fork, no matter how bad things may look.}
  460.    AnyErr := FSClose(NewResRefNum);
  461.   end;{CopyEverything}
  462.  
  463. {=================================MAIN}
  464.  
  465.   var
  466.    str: str255;
  467.    c, FileNameLen: integer;
  468.    tempHand: handle;
  469.  
  470.  begin
  471.   ReturnString := '';
  472.  
  473. {Look up RefNum of current open stack by asking for current resource file.}
  474. {This assumes that the "From" stack has resources. Since we ARE a resource, it must.}
  475.   OldResRefNum := CurResFile;
  476.  
  477. {Ask HyperCard the name of the source stack.}
  478.   tempHand := EvalExpr('the long name of this stack');
  479.   ZeroToPas(tempHand^, OldStackPath);
  480.   delete(OldStackPath, 1, 7);{chop off 'stack "'}
  481.   delete(OldStackPath, length(OldStackPath), 1);{chop off final '"'}
  482.   DisposHandle(tempHand);
  483.   for c := length(OldStackPath) downto 1 do
  484.    if OldStackPath[c] = ':' then
  485.     LEAVE;
  486.   OldStackName := copy(OldStackPath, c + 1, 31);
  487.  
  488. {Try to read first parameter.}
  489.   NewStackName := '';
  490.   NewStackPath := '';
  491.   if ParamPtr^.paramCount > 0 then
  492.    ZeroToPas(ParamPtr^.params[1]^, NewStackPath);
  493.  
  494. {If volume and folder not specified, use path to current stack.}
  495.   if pos(':', NewStackPath) <= 0 then
  496.    begin
  497.     NewStackName := NewStackPath;
  498.     NewStackPath := OldStackPath;
  499.    end;
  500.  
  501. {If even file name not specified, use "New " plus current stack name.}
  502.   if NewStackName = '' then
  503.    begin
  504.     for c := length(NewStackPath) downto 1 do
  505.      if NewStackPath[c] = ':' then
  506.      LEAVE;
  507.     NewStackName := Concat('New ', copy(NewStackPath, c + 1, 27));
  508.    end;
  509.  
  510. {Build full pathname.}
  511.   for c := length(NewStackPath) downto 1 do
  512.    if NewStackPath[c] = ':' then
  513.     LEAVE;
  514.   delete(NewStackPath, c + 1, 100);
  515.   NewStackPath := Concat(NewStackPath, NewStackName);
  516.  
  517. {If there's a second parameter, use it as number of the STAK resource to copy.}
  518.   STAKResID := Unspecified;{Hope they don't really choose this one!}
  519.   if ParamPtr^.paramCount > 1 then
  520.    begin
  521.     ZeroToPas(ParamPtr^.params[2]^, str);
  522.     STAKResID := StrToNum(str);
  523.    end;
  524.  
  525. {Create a new file (both forks) under the new stack name, and copy everything.}
  526.   if CreateNewFile then
  527.    CopyEverything;
  528.  
  529. {Return the result.}
  530.   paramPtr^.returnValue := PasToZero(ReturnString);
  531.  end;{end NewSTAK}
  532.  
  533. {=================================end of unit}
  534. end.